package com.btcode.netty.client;

import com.btcode.common.MyLog;
import com.btcode.log.ILog;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.nio.charset.Charset;

public class HrtNettyClient {
    private ILog log;
    private String clientId;
    private String ip;
    private int port;
    private HrtMsgType msgType;

    private Channel channel = null;
    private Bootstrap bootstrap;
    private EventLoopGroup worker;

    public static void main(String[] args) throws Exception {
        MyLog.getInstance().setRootPath("./log/");
        HrtNettyClient client = new HrtNettyClient("chance", "127.0.0.1", 8123, HrtMsgType.hj212);
        client.init();
        String msgModel = "##0881QN=202011072%08d;ST=32;CN=2051;PW=123456;MN=D0300035978955;Flag=1;CP=&&DataTime=20201107235920;ss-Min=null,ss-Max=null,ss-Avg=2.950166,ss-Cou=null,ss-Flag=N;" +
            "B01TOTAL-Min=null,B01TOTAL-Max=null,B01TOTAL-Avg=4528.0,B01TOTAL-Cou=null,B01TOTAL-Flag=N;snwd-Min=null,snwd-Max=null,snwd-Avg=25.641026,snwd-Cou=null,snwd-Flag=N;sd-Min=null," +
            "sd-Max=null,sd-Avg=54.835163,sd-Cou=null,sd-Flag=N;001-Min=null,001-Max=null,001-Avg=6.705914,001-Cou=null,001-Flag=N;011-Min=null,011-Max=null,011-Avg=10.83,011-Cou=12.322858," +
            "011-Flag=N;065-Min=null,065-Max=null,065-Avg=6.839,065-Cou=23268.498556,065-Flag=N;060-Min=null,060-Max=null,060-Avg=0.481338,060-Cou=0.547688,060-Flag=N;101-Min=null,101-Max=null," +
            "101-Avg=0.0737,101-Cou=0.083859,101-Flag=N;phtem-Min=null,phtem-Max=null,phtem-Avg=28.280664,phtem-Cou=null,phtem-Flag=N;B01-Min=null,B01-Max=null,B01-Avg=7624.669,B01-Cou=null," +
            "B01-Flag=N&&8201\r\n";
        //为了简单，服务端需开启crcOff取消校验
        for (int i = 0; i < 10000000; i++) {
            client.sendMsg(String.format(msgModel, i));
            Thread.sleep(100);
        }
    }

    public HrtNettyClient(String clientId, String ip, int port, HrtMsgType msgType) {
        this.clientId = clientId;
        this.ip = ip;
        this.port = port;
        this.msgType = msgType;
        log = MyLog.getInstance().getLogger(this.getClass().getSimpleName() + ":" + clientId);
    }

    public void init() {
        bootstrap = new Bootstrap();
        //2.定义执行线程组
        worker = new NioEventLoopGroup();
        //3.设置线程池
        bootstrap.group(worker);
        //4.设置通道
        bootstrap.channel(NioSocketChannel.class);
        //5.添加Handler
        bootstrap.handler(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel channel) throws Exception {
                ChannelPipeline pipeline = channel.pipeline();
                pipeline.addLast(msgType.getDecoder()).addLast(msgType.getEncoder());
                pipeline.addLast(new EventHandler());
                log.info("client handler init!");
            }
        });
    }

    private void connect() {
        log.info(clientId + " 正在连接 " + this.ip + ":" + this.port);
        try {
            ChannelFuture channelFuture = bootstrap.connect(this.ip, this.port).sync();
            if (channelFuture.isSuccess()) {
                channel = channelFuture.channel();
                log.info(clientId + " 连接成功：" + channel.localAddress());
                return;
            }
        }
        catch (InterruptedException e) {
            log.error("", e);
        }
        log.info(clientId + " 连接错误");
    }

    public void sendMsg(Object msg) {
        try {
            if (channel == null || !channel.isActive()) {
                connect();
            }
            if (channel != null && channel.isActive() && channel.isWritable()) {
                ByteBuf bb = null;
                if (msg instanceof String) {
                    bb = Unpooled.copiedBuffer((CharSequence) msg, Charset.forName("utf-8"));
                }
                else if (msg instanceof byte[]) {
                    bb = Unpooled.copiedBuffer((byte[]) msg);
                }
                else {
                    log.error("未定义此类消息");
                    return;
                }
                channel.writeAndFlush(bb);
                log.info("[snd] " + msg);
                return;
            }
        }
        catch (Exception e) {
            log.error("消息发送", e);
        }
        log.info(clientId + " 发送错误");
    }

    public void close() {
        try {
            channel.close().sync();
            worker.shutdownGracefully();
        }
        catch (InterruptedException e) {
            log.info(clientId + " close connection in error!");
        }
    }

    class EventHandler extends SimpleChannelInboundHandler {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
            log.info("[rec] " + msg.toString());
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            super.channelActive(ctx);
            log.info(clientId + "连接成功");
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            super.channelInactive(ctx);
            log.info(clientId + "连接断开");
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
            log.error(clientId + "连接异常", cause);
        }
    }

    public enum HrtMsgType {
        //基本字符串协议
        basic("basic", new StringDecoder(), new StringEncoder()),
        //环保协议212
        hj212("hj212", new StringDecoder(), new StringEncoder()),
        //雷电协议
        waterSupple("waterSupple", new ByteArrayDecoder(), new ByteArrayEncoder()),
        //供水协议
        lighting("lighting", new ByteArrayDecoder(), new ByteArrayEncoder());

        private final String name;
        private final MessageToMessageDecoder encoder;

        private final MessageToMessageEncoder decoder;

        HrtMsgType(String name, MessageToMessageDecoder ed, MessageToMessageEncoder de) {
            this.name = name;
            this.encoder = ed;
            this.decoder = de;
        }

        public String getName() {
            return name;
        }

        public MessageToMessageDecoder getEncoder() {
            return encoder;
        }

        public MessageToMessageEncoder getDecoder() {
            return decoder;
        }
    }
}
